#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/sysfs.h>

#define MS519X_V_CHAN(bits, bitshift) {                              \
		.type = IIO_VOLTAGE,                                  \
		.info_mask_separate       = BIT(IIO_CHAN_INFO_RAW),   \
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
		.scan_type = {                                        \
			.sign = 'u',                                  \
			.realbits = (bits),                           \
			.storagebits = 16,                            \
			.shift = (bitshift),                          \
			.endianness = IIO_BE,                         \
		},                                                    \
	}

#define DECLARE_MS519X_CHANNELS(name, bits, bitshift) \
const struct iio_chan_spec name ## _channels[] = { \
	MS519X_V_CHAN(bits, bitshift), \
	IIO_CHAN_SOFT_TIMESTAMP(1), \
}
//static DECLARE_MS519X_CHANNELS(ms5192t, 16, 0);
//static DECLARE_MS519X_CHANNELS(ms5193t, 24, 0);

#define	MS519X_ID_MS5192T (0x4A)
#define	MS519X_ID_MS5193T (0x4B)

// 最高位，BIT 7
#define	MS519X_ENABLE_WRITE_REG_COM (0x00)
#define	MS519X_DISABLE_WRITE_REG_COM (1 << 7)

// 第高二位，BIT 6
#define	MS519X_READ_REG (1 << 6)
#define	MS519X_WRITE_REG (0x00)

// BITS 5～3
#define MS519X_REG_COMST (0x00)
#define MS519X_REG_MODE (1 << 3)
#define MS519X_REG_CFG (2 << 3)
#define MS519X_REG_DATA (3 << 3)
#define MS519X_REG_ID (4 << 3)
#define MS519X_REG_IO (5 << 3)
// 失调寄存器
#define MS519X_REG_OFST (6 << 3)
#define MS519X_REG_FULL (7 << 3)

// 第三低位，BIT 2
#define MS519X_ENABLE_CONTINUOUS_CONV (1 << 2)
#define MS519X_DISABLE_CONTINUOUS_CONV (0x00)

// 低两位，BITS 1～0
#define MS519X_RESERVE (0x00)

#define MS519X_CFG_CH0 (0x0)
#define MS519X_CFG_CH1 (0x1)
#define MS519X_CFG_CH2 (0x2)

// 模式寄存器值
#define MS519X_MOD_CONTINUOUS (0x00)
#define MS519X_MOD_SINGLE (1 << 13)

#define MS519X_16BITS_MASK (0xffff)
#define MS519X_24BITS_MASK (0xffffff)

typedef struct ms519x_s ms519x_t;
typedef struct ms519x_chip_s ms519x_chip_t;
typedef struct ms519x_phy_s ms519x_phy_t;

struct ms519x_phy_s {
	void* phy;
	int (* read_reg)(ms519x_phy_t* , uint8_t reg,
			uint8_t* value, int size);
	int (* write_reg)(ms519x_phy_t* , uint8_t reg,
			uint8_t* value, int size);
	int (* write_raw)(ms519x_phy_t* , uint8_t* value, int size);
};

struct ms519x_chip_s {
	// 需要保存的寄存器
	uint8_t id;
	uint8_t io;
	uint16_t mode;
	uint16_t cfg;
	uint32_t mask;

	// 状态、失调、满量、数据不需要保存
	// uint8_t stat;
	// uint32_t ofst;
	// uint32_t full;
	// uint32_t data;

	// 芯片通信方法，比如SPI、I2C等等
	ms519x_phy_t ms519x_phy;

	// 芯片寄存器操作方法
	int (* get_id)(ms519x_chip_t*);
	int (* get_io)(ms519x_chip_t*);
	int (* set_io)(ms519x_chip_t*, uint8_t );
	int (* get_mode)(ms519x_chip_t*);
	int (* set_mode)(ms519x_chip_t*, uint16_t);
	int (* get_cfg)(ms519x_chip_t*);
	int (* set_cfg)(ms519x_chip_t*, uint16_t);

	uint8_t (* get_stat)(ms519x_chip_t*);
	uint32_t (* get_ofst)(ms519x_chip_t*);
	int (* set_ofst)(ms519x_chip_t*, uint32_t);
	uint32_t (* get_full)(ms519x_chip_t*);
	int (* set_full)(ms519x_chip_t*, uint32_t);
	uint32_t (* get_data)(ms519x_chip_t*);

	int (* reset)(ms519x_chip_t*);
};

struct ms519x_s {
	ms519x_chip_t ms519x_chip;
	struct iio_chan_spec const	*channels;
	int num_channels;
	uint32_t (* get_value)(ms519x_t*, int);
};

const struct iio_chan_spec ms5193t_channels[1] = {
	{
		.type = IIO_VOLTAGE,
		.info_mask_separate       = BIT(IIO_CHAN_INFO_RAW),
		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
		.scan_type = {
			.sign = 'u',
			.realbits = 32,
			.storagebits = 32,
			.shift = 0,
			.endianness = IIO_BE,
		}
	}
};

static int
method_ms519x_phy_read_reg_spi(ms519x_phy_t* ms519x_phy, uint8_t reg,
		uint8_t* value, int size)
{
	uint8_t cmd = MS519X_READ_REG | reg;
	struct spi_device* spi = (struct spi_device*)ms519x_phy->phy;

	spi_write(spi, &cmd, sizeof(cmd));
	spi_read(spi, value, size);
	return 0;
}

static int
method_ms519x_phy_write_reg_spi(ms519x_phy_t* ms519x_phy, uint8_t reg,
		uint8_t* value, int size)
{
	uint8_t cmd = MS519X_WRITE_REG | reg;
	struct spi_device* spi = (struct spi_device*)ms519x_phy->phy;

	spi_write(spi, &cmd, sizeof(cmd));
	spi_write(spi, value, size);
	return 0;
}

static int
method_ms519x_phy_write_raw_spi(ms519x_phy_t* ms519x_phy,
		uint8_t* value, int size)
{
	struct spi_device* spi = (struct spi_device*)ms519x_phy->phy;
	spi_write(spi, value, size);
	return 0;
}

static int
method_ms519x_chip_get_id(ms519x_chip_t* ms519x_chip)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	return ms519x_phy -> read_reg(ms519x_phy, MS519X_REG_ID,
			&ms519x_chip->id, sizeof(ms519x_chip->id));
}

static int
method_ms519x_chip_get_io(ms519x_chip_t* ms519x_chip)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	return ms519x_phy -> read_reg(ms519x_phy, MS519X_REG_IO,
			&ms519x_chip->io, sizeof(ms519x_chip->io));
}

static int
method_ms519x_chip_set_io(ms519x_chip_t* ms519x_chip, uint8_t io)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	int result = 0;

	result = ms519x_phy -> write_reg(ms519x_phy, MS519X_REG_IO,
			&io, sizeof(io));

	if (result == 0)
	{
		ms519x_chip->io = io;
	}

	return result;
}

static int
method_ms519x_chip_get_mode(ms519x_chip_t* ms519x_chip)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint8_t value[2];
	int result = 0;

	result = ms519x_phy -> read_reg(ms519x_phy, MS519X_REG_MODE,
			value, sizeof(value));

	ms519x_chip->mode = (value[0] << 8) | value[1];

	return result;
}

static int
method_ms519x_chip_set_mode(ms519x_chip_t* ms519x_chip, uint16_t mode)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint8_t value[2];
	int result = 0;

	value[0] = mode >> 8;
	value[1] = mode & 0xff;
	result = ms519x_phy -> write_reg(ms519x_phy, MS519X_REG_MODE,
			value, sizeof(value));

	if (result == 0)
	{
		ms519x_chip->mode = mode;
	}

	return result;
}

static int
method_ms519x_chip_get_cfg(ms519x_chip_t* ms519x_chip)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint8_t value[2];
	int result = 0;

	result = ms519x_phy -> read_reg(ms519x_phy, MS519X_REG_CFG,
			value, sizeof(value));

	ms519x_chip->cfg = (value[0] << 8) | value[1];

	return result;
}

static int
method_ms519x_chip_set_cfg(ms519x_chip_t* ms519x_chip, uint16_t cfg)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint8_t value[2];
	int result = 0;

	value[0] = cfg >> 8;
	value[1] = cfg & 0xff;
	result = ms519x_phy -> read_reg(ms519x_phy, MS519X_REG_CFG,
			value, sizeof(value));

	if (result == 0)
	{
		ms519x_chip->cfg = cfg;
	}

	return 0;
}

static uint8_t
method_ms519x_chip_get_stat(ms519x_chip_t* ms519x_chip)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint8_t stat = 0;
	ms519x_phy -> read_reg(ms519x_phy, MS519X_REG_COMST,
			&stat, sizeof(stat));
	return stat;
}

static uint32_t
ms519x_chip_get_value32(ms519x_chip_t* ms519x_chip, uint8_t reg)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint32_t value_32 = 0;
	uint8_t value[4];

	switch (ms519x_chip->mask)
	{
		case MS519X_16BITS_MASK:
			ms519x_phy -> read_reg(ms519x_phy, reg,
					value, 2);
			value_32 = (value[0] << 8) | value[1];
			break;
		case MS519X_24BITS_MASK:
			ms519x_phy -> read_reg(ms519x_phy, reg,
					value, 3);
			value_32 = (value[0] << 16) | (value[1] << 8) | value[2];
			break;
		default:
			break;
	}

	return value_32;
}

static int
ms519x_chip_set_value32(ms519x_chip_t* ms519x_chip,
		uint8_t reg, uint32_t value_32)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint8_t value[4];
	value_32 &= ms519x_chip->mask;

	switch (ms519x_chip->mask)
	{
		case MS519X_16BITS_MASK:
			value [0] = (value_32 >> 8) & 0xff;
			value [1] = value_32 & 0xff;
			return ms519x_phy -> write_reg(ms519x_phy, reg,
					value, 2);
		case MS519X_24BITS_MASK:
			value [0] = (value_32 >> 16) & 0xff;
			value [1] = (value_32 >> 8) & 0xff;
			value [2] = value_32 & 0xff;
			return ms519x_phy -> write_reg(ms519x_phy, reg,
					value, 3);
		default:
			return -1;
	}
}

static uint32_t
method_ms519x_chip_get_ofst(ms519x_chip_t* ms519x_chip)
{
	return ms519x_chip_get_value32(ms519x_chip, MS519X_REG_OFST);
}

static int
method_ms519x_chip_set_ofst(ms519x_chip_t* ms519x_chip, uint32_t ofst)
{
	return ms519x_chip_set_value32(ms519x_chip, MS519X_REG_OFST, ofst);
}

static uint32_t
method_ms519x_chip_get_full(ms519x_chip_t* ms519x_chip)
{
	return ms519x_chip_get_value32(ms519x_chip, MS519X_REG_FULL);
}

static int
method_ms519x_chip_set_full(ms519x_chip_t* ms519x_chip, uint32_t full)
{
	return ms519x_chip_set_value32(ms519x_chip, MS519X_REG_FULL, full);
}

static uint32_t
method_ms519x_chip_get_data(ms519x_chip_t* ms519x_chip)
{
	return ms519x_chip_get_value32(ms519x_chip, MS519X_REG_DATA);
}

static int
method_ms519x_chip_reset(ms519x_chip_t* ms519x_chip)
{
	ms519x_phy_t* ms519x_phy = &ms519x_chip->ms519x_phy;
	uint8_t rst[4] = {0xff, 0xff, 0xff, 0xff};
	int result = 0;

	result = ms519x_phy -> write_raw(ms519x_phy, rst, sizeof(rst));
	mdelay(5);

	return result;
}

static uint32_t
method_ms519x_get_value(ms519x_t* ms519x, int channel)
{
	ms519x_chip_t* ms519x_chip = &ms519x->ms519x_chip;
	uint16_t channel_16 = (uint16_t)channel & 0x7;

	uint16_t cfg = ms519x_chip->cfg | channel_16;
	ms519x_chip->set_cfg(ms519x_chip, cfg);
	ms519x_chip->get_cfg(ms519x_chip);

	return ms519x_chip->get_data(ms519x_chip);
}


/*
static int ms519x_set_single(ms519x_t* ms519x)
{
	ms519x->reg_mode |= MS519X_MOD_SINGLE;
	ms519x_write_reg_16be(ms519x, MS519X_REG_MOD, ms519x->reg_mode);
	return 0;
}
*/
static int ms519x_init(ms519x_t* ms519x, struct spi_device *spi)
{
	ms519x->ms519x_chip.ms519x_phy. phy = spi;
	ms519x->ms519x_chip.ms519x_phy. read_reg =
		method_ms519x_phy_read_reg_spi;
	ms519x->ms519x_chip.ms519x_phy. write_reg =
		method_ms519x_phy_write_reg_spi;
	ms519x->ms519x_chip.ms519x_phy. write_raw =
		method_ms519x_phy_write_raw_spi;

	ms519x->ms519x_chip. get_id =
		method_ms519x_chip_get_id;
	ms519x->ms519x_chip. get_io =
		method_ms519x_chip_get_io;
	ms519x->ms519x_chip. set_io =
		method_ms519x_chip_set_io;
	ms519x->ms519x_chip. get_mode =
		method_ms519x_chip_get_mode;
	ms519x->ms519x_chip. set_mode =
		method_ms519x_chip_set_mode;
	ms519x->ms519x_chip. get_cfg =
		method_ms519x_chip_get_cfg;
	ms519x->ms519x_chip. set_cfg =
		method_ms519x_chip_set_cfg;

	ms519x->ms519x_chip. get_stat =
		method_ms519x_chip_get_stat;
	ms519x->ms519x_chip. get_ofst =
		method_ms519x_chip_get_ofst;
	ms519x->ms519x_chip. set_ofst =
		method_ms519x_chip_set_ofst;
	ms519x->ms519x_chip. get_full =
		method_ms519x_chip_get_full;
	ms519x->ms519x_chip. set_full =
		method_ms519x_chip_set_full;
	ms519x->ms519x_chip. get_data =
		method_ms519x_chip_get_data;

	ms519x->get_value = method_ms519x_get_value;

	ms519x->ms519x_chip. reset =
		method_ms519x_chip_reset;

	ms519x->ms519x_chip.reset(&ms519x->ms519x_chip);
	ms519x->ms519x_chip.get_id(&ms519x->ms519x_chip);
	ms519x->ms519x_chip.get_io(&ms519x->ms519x_chip);
	ms519x->ms519x_chip.get_mode(&ms519x->ms519x_chip);
	ms519x->ms519x_chip.get_cfg(&ms519x->ms519x_chip);

	switch (ms519x->ms519x_chip.id)
	{
		case MS519X_ID_MS5192T:
			ms519x->ms519x_chip.mask = MS519X_16BITS_MASK;
			break;
		case MS519X_ID_MS5193T:
			ms519x->ms519x_chip.mask = MS519X_24BITS_MASK;
			break;
	}

	ms519x->channels = ms5193t_channels;
	ms519x->num_channels = ARRAY_SIZE(ms5193t_channels);

	dev_info(&spi->dev, "MS519x GET ID 0x%x, IO 0x%x,"
			" MOD 0x%x, CFG 0x%x, STATUS 0x%x\n",
			ms519x->ms519x_chip.id,
			ms519x->ms519x_chip.io,
			ms519x->ms519x_chip.mode,
			ms519x->ms519x_chip.cfg,
			ms519x->ms519x_chip.get_stat(&ms519x->ms519x_chip));

	return 0;
}

static int
drive_ms519x_read_raw(struct iio_dev *indio_dev,
			    struct iio_chan_spec const *chan,
			    int *val,
			    int *val2,
			    long m)
{
	int ret = 0;
	ms519x_t* ms519x = iio_priv(indio_dev);
	uint32_t value = 0;

	switch (m) {
	case IIO_CHAN_INFO_RAW:
		ret = iio_device_claim_direct_mode(indio_dev);
		if (ret)
			return ret;

		value = ms519x->get_value(ms519x, 0x01);
		dev_info(&indio_dev->dev, "STATUS = 0x%x\n",
				ms519x->ms519x_chip.cfg);
		iio_device_release_direct_mode(indio_dev);
		*val = ret;
		if (ret < 0)
			return ret;
		/*
		*val = be16_to_cpu(st->rx_buf[0]);
		*val = *val >> chan->scan_type.shift;
		*val &= GENMASK(chan->scan_type.realbits - 1, 0);
		*/
		return IIO_VAL_INT;
/*
	case IIO_CHAN_INFO_SCALE:
		ret = ms519x_get_range(st);
		if (ret < 0)
			return ret;
		*val = ret;
		*val2 = chan->scan_type.realbits;
		return IIO_VAL_FRACTIONAL_LOG2;
*/
	}
	return -EINVAL;
}

static const struct iio_info glob_ms519x_info = {
	.read_raw = &drive_ms519x_read_raw,
};

static int
drive_ms519x_spi_probe(struct spi_device *spi)
{
	ms519x_t *ms519x;
	struct iio_dev *indio_dev;
	int ret;
	static char tem_name[] = "MS519XT";

	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ms519x));
	if (indio_dev == NULL)
		return -ENOMEM;

	spi_setup(spi);

	ms519x = iio_priv(indio_dev);

	dev_info(&spi->dev, "MS519X_SPI CS:%d, max_speed:%d, bpw:%d,"
			"rt:%d, mode:%d\n",
			spi->chip_select, spi->max_speed_hz, spi->bits_per_word,
			spi->rt, spi->mode);

	if (ms519x_init(ms519x, spi) != 0)
		return -EINVAL;

	indio_dev->name = tem_name;
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->channels = ms519x->channels;
	indio_dev->num_channels = ms519x->num_channels;
	indio_dev->info = &glob_ms519x_info;
/*
	st->reg = devm_regulator_get(&spi->dev, "vref");
	if (IS_ERR(st->reg))
		return PTR_ERR(st->reg);

	ret = regulator_enable(st->reg);
	if (ret)
		return ret;

	ret = iio_triggered_buffer_setup(indio_dev, NULL,
			&ms519x_trigger_handler, NULL);
	if (ret)
		goto error_disable_reg;
*/
	ret = iio_device_register(indio_dev);
	if (ret)
		goto error_cleanup_buffer;

	spi_set_drvdata(spi, indio_dev);

	return 0;

error_cleanup_buffer:
	iio_triggered_buffer_cleanup(indio_dev);
//error_disable_reg:
//	regulator_disable(st->reg);

	return ret;
}
/*
static const struct spi_device_id ms519x_id_table[] = {
	{ "ms5192t", ID_MS5192T },
	{ "ms5193t", ID_MS5193T },
	{}
};
MODULE_DEVICE_TABLE(spi, ms519x_id_table);
*/
static const struct of_device_id ms519x_of_match[] = {
	{ .compatible = "ruimeng,ms5192t" },
	{ .compatible = "ruimeng,ms5193t" },
	{ },
};
MODULE_DEVICE_TABLE(of, ms519x_of_match);

static struct spi_driver ms519x_driver = {
	.driver = {
		.name = "ms519x",
		.of_match_table = ms519x_of_match,
//		.pm = ,
	},
	.probe = drive_ms519x_spi_probe,
//	.remove = ms519x_remove,
//	.id_table = ms519x_id_table,
};
module_spi_driver(ms519x_driver);

MODULE_AUTHOR("Yize Niu <niuyize@loongson.cn>");
MODULE_DESCRIPTION("Analog Devices MS519x ADC");
MODULE_LICENSE("GPL v2");

